"""
Copyright 2009, Jay Shurtz.
 
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.

Module for all Struct and related block-type (non-leaf) hatch nodes.
"""
#----------------------------------------------------------------------

id = "$Id: hatch_struct.py 667 2010-07-01 00:12:17Z jayshurtz $"
# $URL: http://hatch.googlecode.com/svn/tags/taggle_release_2.3/hatch/hatch_nodes/hatch_struct.py $
# $Author: jayshurtz $
version = " version ".join(id.split()[1:3])

#----------------------------------------------------------------------

import to_hatch_structure
from hatch_node_physical import *
from hatch_register import *    # For 'Register' function.

#----------------------------------------------------------------------

class Struct(HatchNodePhysical):
    """ A struct is a register pack/block.
        It is not merely an array because it may contain elements of non-uniform length.
        A struct may also contain other structs (as child nodes.)
        As it cannot be the lowest leaf in the hierachy, a struct's 'hatch_leaf' property 
        should be 'False' to indicate that it must have at least one child node.
    """
    def __init__(self, name, *childNodes, **properties):
        """ """
        HatchNodePhysical.__init__(self, name, hatch_leaf=False, *childNodes, **properties)

    def Register(self, name, **properties):
        """ Allow creation of a Register childNode after instantiation.
        """
        r = Register(name, **properties)
        self.add_content(r)
        return r # return the created element as a convenience.

    def Struct(self, name, *childNodes, **properties):
        """ Allow creation of a struct childNode after instantiation.
        """
        #if self.properties.get(PACKED, False):  # False if not defined.
        #    raise AttributeError, hatchNodeErrorString(self, "Packed nodes cannot contain Structs.")   # TODO use this logic & simplify Pack class ?
        #else:
        s = Struct(name, *childNodes, **properties)
        self.add_content(s)
        return s # return the created element as a convenience.

    def Pack(self, name, *childNodes, **properties):
        """ Allow creation of a pack childNode after instantiation.
        """
        p = Pack(name, *childNodes, **properties)
        self.add_content(p)
        return p # return the created element as a convenience.


class Pack(Struct):
    """ A Pack is a node that contains at least one Register.  The
        Register(s) are packed into a single address location.  As
        such the total bit width of a pack cannot exceed the 'data bus
        width', which is a function of the target bus object.

        A pack cannot contain other packs or structs.  It can only contain 
        hatch leaf nodes (such as Registers.) This is checked by the property 
        checker visitor using the PACKED and HATCH_LEAF properties.

        Note that a Pack is basically a Struct with the property packed=True.
    """
    def __init__(self, name, *childNodes, **properties):
        """ """
        Struct.__init__(self, name, packed=True, *childNodes, **properties)  # All packs are 'packed'.

    def Struct(self, name, *childNodes, **properties):
        """ Do not allow creation of a struct childNode after instantiation.
        """
        raise AttributeError, hatchNodeErrorString(self, "Packs can only have Registers as children.")

    def Pack(self, name, *childNodes, **properties):
        """ Allow creation of a pack childNode after instantiation.
        """
        raise AttributeError, hatchNodeErrorString(self, "Packs can only have Registers as children.")


# TODO - add functions for these classes to Struct, like with Register() and Pack() (and test them below.)
if False:
    class Mux(Struct):
        """ A Mux is a node that contains ...
        """
        def __init__(self, name, *childNodes, **properties):
            """ """
            Struct.__init__(self, name, *childNodes, **properties)

    class Chip(Struct):
        """ A Chip is a node that contains ... such as an FPGA.
        """
        def __init__(self, name, *childNodes, **properties):
            """ """
            Struct.__init__(self, name, *childNodes, **properties)

    class Board(Struct):
        """ A Board is a node that contains ... such as a PCB.
        """
        def __init__(self, name, *childNodes, **properties):
            """ """
            Struct.__init__(self, name, *childNodes, **properties)


# Main for testing.
if __name__ == "__main__":

    # Test Struct class
    s = Struct(name='timers', packed=False, target='small apb slave', input_ports="""tick""", prefix="test_a_", header=""" bla """)
    t = Struct('timers2', s, target='small apb slave', input_ports="""tick""", prefix="test_a_", header=""" bla """)
    t.Register('test_e', width=10, hw='c', default=0xe)
    s.Pack('nomo', Register('test_d', width=10, hw='s', default=0xd), prop='nomoprop')
    t.Struct('bosco', prop='prop')

    print t.info()

        # packed
        
        # not packed

        # packable

        # unpackable


    # Test Pack class
    p = Pack('nombre', Register('test_f', width=10, hw='i', default=0xf), prop='prop')
    p.Register('test_d', width=10, hw='s', default=0xd)
    p.Register('test_e', width=10, hw='c', default=0xe)
            
    try:
        p.Pack('nomo')
    except AttributeError, e:
        print str(e)

    print p.info()


    if False:
        hatchBlock = Struct(name='timers', packed=False, target='small apb slave', input_ports="""tick""", prefix="test_a_", header=""" bla """)
        hatchBlock.Register('real_time_clock', width=32, sw='rw', comment="""This is the real time clock and is incremented at a rate of 32kHz""", \
            auto='i', auto_inc_on='tick', rollover=1, hw='r')
        hatchBlock.Struct("sub block",
            Struct("wer",   # elaborate should name this?
                Register('test_a', width=10, hw='s', default=0xa),
                Register('test_b', width=10, hw='c', default=0xb),
                Register('test_c', width=10, hw='i', default=0xc, sw='w'),
                hw='d', # all the registers in this pack should get hw=d
                packed=True),
            hw='w', # all the registers in this block should get hw=w
            # no sw action is specified but all these
            # registers should be readable.
            packed=True)
        bBlock = Struct("another_sub_block", 
            Register('test_d', width=10, hw='s', default=0xd),
            Register('test_e', width=10, hw='c', default=0xe),
            Register('test_f', width=10, hw='i', default=0xf),
            hw='c', # all the registers in this block should get hw=c
            sw='rw',
            packed=True)
        hatchBlock.add_content(bBlock)

        print n.info()
        n.elaborate()


